package com.foreveross.bsl.push.application.impl.channel.openfire;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.foreveross.bsl.common.utils.mapper.JsonMapper;
import com.foreveross.bsl.push.application.PushModuleException;
import com.foreveross.bsl.push.application.impl.DsRouterHelper;
import com.foreveross.bsl.push.application.impl.DsRouterPushId;
import com.foreveross.bsl.push.application.impl.channel.BaseChannel;
import com.foreveross.bsl.push.application.impl.channel.SendException;
import com.foreveross.bsl.push.application.impl.channel.SendFeedbackable;
import com.foreveross.bsl.push.application.impl.channel.SendTarget;
import com.foreveross.bsl.push.application.impl.channel.SendTarget.TargetIdentity;
import com.foreveross.bsl.push.domain.entity.channel.OpenfireConfig;
import com.google.common.collect.Maps;

@Component
public class OpenfireChannel extends BaseChannel {

	private final Logger log = LoggerFactory.getLogger(this.getClass());

	public final static String KEY_HOST="openfire.host";
	public final static String KEY_PORT="openfire.port";
	
	private final static MessageListener DUMMY_MSG_LISTENER=new MessageListener() {
		@Override
		public void processMessage(Chat paramChat, Message paramMsg) {
		}
	};

	private String serverHost;

	private int serverPort;

	private final Map<String, Connection> connectionPool = Maps.newConcurrentMap();
	private final byte[] locker=new byte[0];
	private final JsonMapper jsonMapper;

	@Inject
	private SendFeedbackable feedbacker;

	public OpenfireChannel() {
		jsonMapper=JsonMapper.nonEmptyMapper();
	}

	@Override
	public String getChannelId() {
		return "openfire";
	}

	@Override
	protected void doSend(SendTarget target, com.foreveross.bsl.push.domain.Message msg) {
		DsRouterPushId[] pushIdArray=buildDsRouterPushIds(target).toArray(new DsRouterPushId[0]);
		try {
			Connection conn=this.buildOpenfireConnection(target.getAppId());
			for(TargetIdentity ti : target.getTargets()){
				DsRouterPushId drp=this.getDsRouterPushByPushId(ti.getPushId(), pushIdArray);
				try {
					if(log.isTraceEnabled()){
						log.trace("appId:{}, deviceId:{}, token: {}",target.getAppId(), ti.getDeviceId(), ti.getToken());
					}
					Chat newChat = conn.getChatManager().createChat(ti.getToken(), DUMMY_MSG_LISTENER);
					String msgJson=convertMessage(msg);
					feedbacker.sentSuccessfully(new Date(), drp);
					newChat.sendMessage(msgJson);
					log.trace("发送消息成功:{}", msgJson);
					if(log.isTraceEnabled()){
						log.trace("推送到终端的消息内容({}bytes): {}", msgJson.getBytes().length, msgJson);
					}
				} catch (Exception e) {
					feedbacker.unrecoverableFailure(e.getMessage(), e, drp);
				}
			}
		} catch (XMPPException e) {
			if(e.getMessage().contains("not-authorized")){
				feedbacker.unrecoverableFailure(e.getMessage(), e, pushIdArray);
			}
			else{
				throw new SendException(e);
			}
		}
	}

	private DsRouterPushId getDsRouterPushByPushId(String pushId, DsRouterPushId[] pushIdArray){
		for(DsRouterPushId drp : pushIdArray){
			if(drp.getPushId().equals(pushId)){
				return drp;
			}
		}
		throw new IllegalArgumentException("在"+pushIdArray.length+"个pushIdArray中找不到pushId："+pushId);
	}

	private String convertMessage(com.foreveross.bsl.push.domain.Message msg){
		// 处理指定需要推过去的extras中的属性
		com.foreveross.bsl.push.domain.Message t=new com.foreveross.bsl.push.domain.Message();
		t.setId(msg.getId());
		t.setTitle(msg.getTitle());
		t.setMessageType(msg.getMessageType());
		String[] deps = msg.getDirectExtrasPropertys();
		if (deps != null && deps.length > 0) {
			for (String key : deps) {
				Object value = msg.getExtra(key);
				if (value != null) {
					t.putExtra(key, value);
				}
			}
		}
		return jsonMapper.toJson(t);
	}

	private List<DsRouterPushId> buildDsRouterPushIds(SendTarget target){
		List<DsRouterPushId> pushIds=new ArrayList<DsRouterPushId>(target.getTargets().size());
		String dsRouter=DsRouterHelper.getCurrentDsRouter();
		for(SendTarget.TargetIdentity ti : target.getTargets()){
			pushIds.add(new DsRouterPushId(ti.getPushId(), dsRouter));
		}
		return pushIds;
	}

	@Override
	protected void doClose() throws Exception {
		log.debug("{}渠道的Connection个数:{}", this.getChannelId(), this.connectionPool.size());
		for(String key : this.connectionPool.keySet()){
			Connection conn = this.connectionPool.get(key);
			try {
				conn.disconnect();
			} catch (Exception e) {
				log.error("关闭连接" + key + "异常", e);
			}
		}
		this.connectionPool.clear();
	}

	private Connection buildOpenfireConnection(String appId) throws XMPPException{
		Connection conn = this.connectionPool.get(appId);
		if(conn==null){
			synchronized (locker) {
				conn=this.connectionPool.get(appId);
				if(conn==null){
					log.debug("instance connection...");
					// Create the configuration for this new connection
					ConnectionConfiguration config = new ConnectionConfiguration(getServerHost(), getServerPort());
					config.setCompressionEnabled(true);
					config.setSASLAuthenticationEnabled(true);
					conn = new XMPPConnection(config);
					this.connectionPool.put(appId, conn);
				}
			}
		}
		synchronized(locker){
			if (!conn.isConnected()){
				log.debug("begin connect to Openfire server {}:{}...", new Object[]{this.serverHost, this.serverPort});
				conn.connect();
			}
			if(!conn.isAuthenticated()) {
				OpenfireConfig cfg=this.getAppChannelConfig(appId).getOpenfireConfig();
				if(cfg==null){
					throw new PushModuleException("应用:"+appId+"的Openfire的配置未设置");
				}
				log.debug("begin login to server by user {}:{}...", cfg.getUsername(), cfg.getPassword());
				conn.login(cfg.getUsername(), cfg.getPassword());
				log.debug("应用:{}的Openfire的连接已准备好!", appId);
			}
		}
		return conn;
	}

	public int getServerPort() {
		return serverPort;
	}

	public void setServerPort(int serverPort) {
		this.serverPort = serverPort;
	}

	public String getServerHost() {
		return serverHost;
	}

	public void setServerHost(String serverHost) {
		this.serverHost = serverHost;
	}

	@PostConstruct
	public void init(){
		setServerHost(this.getChannelConfig().getProperty(KEY_HOST));
		int port=5222;
		try {
			Integer.parseInt(getChannelConfig().getProperty(KEY_PORT, "5222"));
		} catch (NumberFormatException e) {
		}
		setServerPort(port);
		if(this.serverHost==null){
			log.warn("Openfire主机地址未设置！");
		}
		else{
			log.info("Openfire主机地址:{},端口:{}", this.serverHost, this.serverPort);
		}
	}

}
